package utils

//
// fault tolerance RPC.
// can deal with crash of the server, with return false
// can deal with time out
//

import (
	"net/rpc"
	"time"
)

const (
	rpc_timeout = 5000
)

type ClientEnd struct {
	endname string // this end-point's name, ip:port
	client  *rpc.Client
}

// Call send an RPC, wait for the reply.
// the return value indicates success; false means that
// no reply was received from the server.
func (e *ClientEnd) Call(svcMeth string, args interface{}, reply interface{}) bool {
	if e.client == nil {
		if e.endname != "" {
			client, err := rpc.DialHTTP("tcp", e.endname)
			if err != nil {
				LogC(Red, "Failed to connect to server: %v\n", err)
				return false
			}
			e.client = client
		}
	}
	call := e.client.Go(svcMeth, args, reply, nil)

	select {
	case <-call.Done:
		if call.Error != nil {
			LogC(Red, "Err: %v\n", call.Error)
			return false
		}
		return true
	case <-time.After(time.Duration(rpc_timeout) * time.Millisecond):
		LogC(Red, "RPC call %s timed out\n", svcMeth)
		return false
	}
}

func MakeEnd(endname string) *ClientEnd {
	e := &ClientEnd{}
	e.endname = endname
	if e.client == nil {
		if e.endname != "" {
			client, err := rpc.DialHTTP("tcp", e.endname)
			if err != nil {
				LogC(Red, "Failed to connect to server: %v\n", err)
				client = nil
			}
			e.client = client
		}
	}
	return e
}

func (e *ClientEnd) Close() {
	if e.client != nil {
		err := e.client.Close()
		if err != nil {
			LogC(Red, "Failed to close client: %v\n", err)
		}
		return
	}
	LogC(Red, "The cilent end %s is nil when closing it.\n", e.endname)

}
